import { HttpService, Injectable } from '@nestjs/common';
import * as crypto from 'crypto'
import { URL } from 'url';


@Injectable()
export class PaymentService {
    private wxJSapiUrl = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi';
    constructor(
        private httpService:HttpService,
    ){}

    wxchatCreateAuthorization(url:string, method:string, mchid:string, body:object|string, serialNo:string, privateKey:string, timeStamp:number) {
        const urlObj = new URL(url)
        let bodyParamsStr = ''
        if(body) {
            if(typeof body == 'object') {
                if(Object.keys(body).length > 0) {
                    bodyParamsStr = JSON.stringify(body)
                }
            }else {
                bodyParamsStr = body
            }
        }

        const nonceStr = this.generate()

        const signature = crypto.createSign('RSA-SHA256')
        .update(`${method}\n${urlObj.pathname}\n${timeStamp}\n${nonceStr}\n${bodyParamsStr}\n`)
        .sign(privateKey, 'base64')

        return `WECHATPAY2-SHA256-RSA2048 mchid="${mchid}",serial_no="${serialNo}",nonce_str="${nonceStr}",timestamp="${timeStamp}",signature="${signature}"`
    }

    async wechatPayment(params:{appid:string, mchid:string, orderNo:string; total:number; notifyUrl : string; openid:string; privateKey:string; serialNo:string; description? : string;}) {
        const postData = {
            mchid : params.mchid,
            appid : params.appid,
            notify_url : params.notifyUrl,
            out_trade_no : params.orderNo,
            description : params.description || '充值余额支付',
            amount : {
                total : params.total * 100
            },
            payer : {
                openid : params.openid
            }
        }

        const timeStamp = Math.floor((new Date()).getTime() / 1000)

        try {
            const { data } = await this.httpService.post(
                this.wxJSapiUrl,
                postData,
                {
                    timeout : 10000,
                    headers : {
                        'Content-Type':'application/json',
                        'Accept':'application/json',
                        Authorization : this.wxchatCreateAuthorization(this.wxJSapiUrl, 'POST', params.mchid, postData, params.serialNo, params.privateKey, timeStamp)
                    }
                }
            ).toPromise()

            if(data.prepay_id) {

                const nonceStr = this.generate()

                return {
                    timeStamp : String(timeStamp),
                    nonceStr : nonceStr,
                    package : 'prepay_id=' + data.prepay_id,
                    signType : 'RSA',
                    paySign : crypto.createSign('RSA-SHA256')
                    .update(`${params.appid}\n${timeStamp}\n${nonceStr}\nprepay_id=${data.prepay_id}\n`)
                    .sign(params.privateKey, 'base64')
                }
            }


        } catch (error) {
            console.log(error)
        }

        return null
    }


    wxNotify(body:WxPay.NotifyBody, headers:WxPay.NotifyHeaders, apiV3Key:string) {
        
        const encrypted = Buffer.from(body.resource.ciphertext, 'base64')
        let decipher = crypto.createDecipheriv('aes-256-gcm', apiV3Key, body.resource.nonce)
        decipher.setAuthTag(encrypted.slice(-16))
        decipher.setAAD(Buffer.from(body.resource.associated_data))
        let output = Buffer.concat([
            decipher.update(encrypted.slice(0, -16)),
            decipher.final()
        ])

        return JSON.parse(output.toString())
    }

    async wxV3Certificates(params:{mchid:string; privateKey:string; serialNo:string;}) {
        const url = 'https://api.mch.weixin.qq.com/v3/certificates'
        const timeStamp = Math.floor((new Date()).getTime() / 1000)

        try {
            const res = await this.httpService.get(url,{
                timeout : 10000,
                headers : {
                    'Content-Type':'application/json',
                    'Accept':'application/json',
                    Authorization : this.wxchatCreateAuthorization(url, 'GET', params.mchid, '', params.serialNo, params.privateKey, timeStamp)
                }
            }).toPromise()
    
            console.log(res)
        } catch (error) {
            console.log(error)
        }
    }


    generate() {
        const str = 'QA2ZXSD1W7EDC6743VFRTG8BVN58FHY2YU6GCJ0MD9IKIOL5LP'
        const min = 0
        const max = str.length - 1

        let tmpStr = ''

        for(let i = 0; i < 32; i ++) {
            let index = Math.floor(Math.random() * (max - min)) + min
            tmpStr += str.charAt(index)
        }

        return tmpStr
    }
}
